﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Specialized;
using System.Collections;
using System.Web.UI.WebControls;
using IdeaNMR.Shared;

namespace IdeaNMR.Web.Core
{
    public class SetupFassade : NMRObjectFassade, IEquatable<SetupFassade>
    {
        public byte Dimentionality { get; set; }
        public string Type { get; set; }

        private List<ComponentItem> _components;
        public List<ComponentItem> Components
        { get { return this._components; } }

        public SetupFassade()
            : base(ComponentType.Setup)
        {
            this.Dimentionality = 0;
            this.Type = string.Empty;

            this._components = new List<ComponentItem>();
        }

        /// <summary>
        /// Initializes the Object with the values of the DB entity.
        /// </summary>
        /// <param name="id"></param>
        /// <param name="inmr">ObjectContext - will be created if null.</param>
        /// <exception cref="System.InvalidOperationException">When id is empty</exception>
        /// <exception cref="System.ArgumentException">When entity with given ID is not found.</exception>
        /// <exception cref="System.Unauthorizedaccessexception">When private entity does not belong to user.</exception>
        public override NMRObject Initialize(Guid id, string userName, IdeaNMRdbContainer inmr)
        {
            if (inmr == null) inmr = new IdeaNMRdbContainer();
            Setup result = (Setup)base.Initialize(id, userName, inmr);

            this.Type = result.Type;
            this.Dimentionality = result.Dimentionality;

            #region Components
            // Load components
            this._components.Clear();
            Component c;
            ComponentItem cI;
            ComponentType cT;
            if (!result.NamedComponents.IsLoaded) result.NamedComponents.Load();
            foreach (NamedComponent nc in result.NamedComponents)
            {
                if (!nc.ComponentReference.IsLoaded) nc.ComponentReference.Load();
                c = nc.Component;
                cT = c.GetComponentType();
                cI = new ComponentItem()
                {
                    Id = c.Id,
                    Identifier = nc.Identifier,
                    Content = c.Content,
                    Type = cT,
                    Name = c.Name,
                    Script = c.Script,
                    IsIncludable = (cT == ComponentType.Script) ? ((Script)c).IsIncludable : false
                };
                this._components.Add(cI);
            }
            #endregion

            GetDevelopments(userName, result);
            return result;
        }

        /// <summary>
        /// Shallow copy of only user defined fields.
        /// </summary>
        /// <param name="sf"></param>
        public void CopyTo(SetupFassade sf)
        {
            base.CopyTo(sf);
            sf.Dimentionality = this.Dimentionality;
            sf.Type = this.Type;

            sf._components.Clear();
            sf._components.AddRange(this._components);
        }

        #region ICloneable Members

        //public object Clone()
        //{
        //    SetupFassade clone = new SetupFassade();
        //    clone.Id = this.Id;
        //    clone.Name = this.Name;
        //    clone.Dimentionality = this.Dimentionality;
        //    clone.Info = this.Info;
        //    clone.Type = this.Type;
        //    clone.Script = this.Script;
        //    clone.Datetime = this.Datetime;
        //    clone.IsPrivate = this.IsPrivate;
        //    clone.IsDraft = this.IsDraft;
        //    clone.VersionNumber = this.VersionNumber;
        //    clone.FirstVersionId = this.FirstVersionId;
        //    clone.TemplateId = this.TemplateId;
        //    clone._authors.AddRange(this._authors);
        //    clone._components.AddRange(this._components);
        //    clone._developments.AddRange(this._developments);

        //    return clone;
        //}

        #endregion

        /// <summary>
        ///     Saves the setup to the database.
        /// </summary>
        /// <param name="userName">
        ///     Used to fetch the PRIVATE development versions for this setup after it is saved.
        /// </param>
        /// <remarks>
        ///     <para>
        ///         <paramref name="userName"/> is used because there might be development versions created by different users.
        ///         Only those developed by current user must be selected.
        ///     </para>
        /// </remarks>
        public override NMRObject SaveToDatabase(string userName, IdeaNMRdbContainer inmr)
        {
            if(inmr == null) inmr = new IdeaNMRdbContainer();

            Setup entity = base.GetEntityById<Setup>(this.Id, inmr);

            // if entity is null - means it is first time 
            // the current object is saved to the db
            if (entity == null)  //Create new entry
            {
                entity = new Setup();
                entity.Id = this.Id;

                // Find a FirstVersionId in ObjectContent if it is present in this.FirstVersionId
                Setup fV = null;  // First Version is exists
                if (this.FirstVersionId != Guid.Empty)
                    fV = base.GetEntityById<Setup>(this.FirstVersionId, inmr);
                else fV = entity;
                if (fV == null) throw new InvalidOperationException("There is not FirstVersion with given Id.");
                entity.FirstVersion = fV;

                Setup tE = null;
                if (this.TemplateId != Guid.Empty)
                    tE = base.GetEntityById<Setup>(this.TemplateId, inmr);
                else tE = entity;
                if (tE == null) throw new InvalidOperationException("There is not Template with given Id.");
                entity.Template = tE;
            }
            else // Clear authors
            {
                /**************IMPORTANT**************/
                ValidateAuthorization(entity, userName);

                if (!entity.Authors.IsLoaded) entity.Authors.Load();
                NumberedAuthor[] nas = entity.Authors.ToArray<NumberedAuthor>();
                foreach (NumberedAuthor na in nas) inmr.DeleteObject(na);

                if (!entity.NamedComponents.IsLoaded) entity.NamedComponents.Load();
                NamedComponent[] ncs = entity.NamedComponents.ToArray<NamedComponent>();
                foreach (NamedComponent nc in ncs) inmr.DeleteObject(nc);
            }

            FillEntity(entity, inmr);
            
            inmr.SaveChanges();

            this.GetDevelopments(userName, entity);
            return entity;
        }

        protected void FillEntity(Setup entity, IdeaNMRdbContainer source)
        {
            base.FillEntity(entity, source);

            entity.Dimentionality = this.Dimentionality;
            entity.Type = this.Type;

            // Components
            foreach (ComponentItem cI in this._components)
            {
                NamedComponent nC = new NamedComponent();
                nC.Id = Guid.NewGuid();
                nC.Identifier = cI.Identifier;
                nC.Component = (from c in source.BaseObjects.OfType<Component>()
                                where c.Id == cI.Id
                                select c).FirstOrDefault();

                entity.NamedComponents.Add(nC);
            }
        }

        #region IEquatable<SetupFassade> Members
        public override int GetHashCode()
        {
            int result = base.GetHashCode();
            result ^= this.Dimentionality.GetHashCode();
            result ^= this.Type.GetHashCode();

            foreach (ComponentItem cI in this._components)
                result ^= cI.GetHashCode();

            return result;
        }

        public override bool Equals(object obj)
        {
            if (!(obj is SetupFassade))
                throw new InvalidCastException("The 'obj' argument is not a SetupFassade object.");
            else
                return Equals(obj as SetupFassade);
        }

        public bool Equals(SetupFassade other)
        {
            if (other == null) return false;
            return this.GetHashCode() == other.GetHashCode();
        }
        #endregion
    }
}
